home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
c
/
nos042_s
/
nntpcli.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-16
|
67KB
|
2,367 lines
/*
* Client routines for Network News Tranfer Protocol ala RFC977
*
* Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*
* Changes copyright 1990 Bernie Roehl, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*
* Revision history:
*
* May 11, 1990 - br checked for invalid chars in news filenames
*
* May 10, 1990 - br changed date stamp in 'From ' lines to
* seconds since GMT (to make parsing and expiry easier)
*
* May 9, 1990 - br added locking of nntp.dat and history files,
* second parameter to NNTP DIR, fixed bug in updating of
* nntp.dat
*
* early May, 1990 -- br added NNTP TRACE, NNTP DIR,
* server-specific newsgroups and connection windows,
* locking of newsgroup files using mlock() and rmlock(),
* date stamping of 'From ' lines, increased stack space,
* updating of nntp.dat only on successful sessions.
*
* July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
*
* 20 May 92 1.2 GT Output file in "snews" batch format.
* 21 May 92 1.3 GT Fix 1.2.
* 07 Jun 92 1.4 GT "connect_wait_val" timeout on connect () call.
* 11 Jun 92 1.5 GT Booked out in error.
* 15 Jun 92 1.6 GT Flush history file write.
*
* 21-Jul-92 Chris Sowden (csowden@mouse.demon.co.uk)
* Added NNTP BATCH and NNTP VERBOSE commands.
* Re-wrote article fetching section to allow overlapped
* operation (batch mode).
* Added a 64K bit history hash table.
* Added 2 mins time adjustment to allow for a fast clock.
* Added status message showing articles, bytes and rate.
* Lock "nntp.dat" for duration of session to prevent races.
* Numerous changes to speed things up.
*
* 08 Aug 92 1.8 MM Force disc writes of history file.
*
* 11-Sep-92 Chris Sowden (csowden@mouse.demon.co.uk)
* Look for newsgroup list in data file first.
* Allow multiple NEWNEWS requests.
* Added NNTP NEWGROUPS command.
* Write directly to batch file in newsbatch mode.
* Additional messages in verbose mode.
* Remove zero length files.
*
* 14 Sep 92 GT Use GMT form of NEWNEWS and NEWGROUPS commands.
*
* 14-Sep-92 Chris Sowden (csowden@mouse.demon.co.uk)
* Added counting to verbose received mesage.
*
* 15 Sep 92 Chris Sowden (csowden@mouse.demon.co.uk)
* Fixed reentrancy problem.
*
* 15 Sep 92 GT Fixed race condition on GMT vs. local time reading
* between news and mail.
*
* 13-Nov-92 Chris Sowden (csowden@mouse.demon.co.uk)
* Added SAFETY command, expanded BATCH command
* Uses child processes for initialisation and requests
* New KILL file and GET file for selective article fetching
* Uses correct "#! rnews nnnn" style newsbatch separator
* Improved history file handling (especially for duplicates)
* Added progress messages/statistics in verbose mode
* Now logs all error messages and news summary
*
* 01-Dec-92 Chris Sowden (csowden@mouse.demon.co.uk)
* Improved the opening sequence of KILL and GET files
* Corrected error levels of file opens
* Close temporary new message file
* Tried to reduce likelihood of lock files being left around
* Removed extra linefeed from batch files
*
* 08 May 93 1.13 GT Fix warnings.
* Reinstate 2 minute fudge factor.
* Drop the word "article" from the "news
* arrived" message.
* Fix signed variables problem.
*
* 07 Nov 93 1.14 GT Display NEWNEWS results when "nntp verb on".
* Fix dot transparency. Fix blank lines in error log.
*
* $Id: nntpcli.c 1.14 94/01/04 14:09:53 ROOT_DOS Exp $
*
* -------------------------------------------------------------------------
*
* Atari ST NOS Version by David Nash - dnash@chaos.demon.co.uk
*
* 16.08.94 - Removed Local profile code, mail style message batches,
* nntp_reg call
* 25.07.94 - Improve status messages in donntpstat,
* Local profile code conditional on NNTP_PROF (default disabled)
* 03.07.94 - response >= 400 now trace level 1 (from level 2)
* 05.06.94 - newsbatch default changed to output in rnews fromat
* 05.08.93 - articles for posting now suffixed .nwk and new format
* 09.05.93 - added prototypes for __regargs
* 18.04.93 - rnews batched or unbatched format now selectable
* 09.04.93 - added donnstatus to report the status of a nntp session
* 10.03.93 - added Profile and Post subcommands from ANOS version by G1YYH
* 18.07.93 - post articles from Newsqdir, suffix now .nws. Fixed nntplist
*
* -------------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h> /* ST NOS */
#include <time.h>
#ifndef ATARI /* ST NOS */
#include <sys/timeb.h>
#endif
#include <ctype.h>
#include <string.h> /* for strchr() */
#include <io.h>
#ifdef MSDOS
#include <dir.h>
#endif
#include "global.h"
#include "timer.h"
#include "cmdparse.h"
#include "commands.h"
#include "socket.h"
#include "usock.h"
#include "netuser.h"
#include "proc.h"
#include "smtp.h"
#include "files.h"
#include "session.h" /* ST NOS */
#include "main.h" /* for main_exit */
void filedir(char *, int, char *); /* from filedir.c */
/* The grouplist must fit on a line like "NEWNEWS <grouplist> 000000 000000 GMT\n" */
#define NNTPMAXLEN 512
#define NNTPMAXGROUPLIST NNTPMAXLEN-28
static struct nntpservers {
struct timer nntpcli_t;
char *name;
char *groups;
int lowtime, hightime; /* for connect window */
struct nntpservers *next;
};
struct textline {
struct textline *nextline; /* pointer to next text line */
char text[NNTPMAXLEN]; /* array holding text */
};
struct nntprequest; /* hide nntprequest struct tag */
#define NULLTEXTLINE (struct textline *) 0
#define MINTEXTLINESIZE (sizeof(struct textline)-NNTPMAXLEN)
#define NULLNNTP (struct nntpservers *)NULL
#define NEWSBATCH TRUE /* output for batching newsreader */
#define NEWSBATCHFORMAT "#! rnews %010lu\n"
static int newsbatch = 1; /* output for offline unbatch */
struct nntpservers *Nntpservers = NULLNNTP;
static char *Nntpgroups = NULLCHAR;
static unsigned short nntptrace = 1;
static int nntpbatch = 0;
static int nntpbatchsize = 2;
static int nntpnewgroups = 0;
static int nntpsafety = 1;
static int nntpverbose = 1;
static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
ext_dokicks(void);
static void nntptick(void *tp);
static int dobatch(int argc,char *argv[],void *p);
static int doadds(int argc,char *argv[],void *p);
static int dondir(int argc,char *argv[],void *p);
static int dodrops(int argc,char *argv[],void *p);
static int dogroups(int argc,char *argv[],void *p);
static int dokicks(int argc,char *argv[],void *p);
static int dolists(int argc,char *argv[],void *p);
static int donewgroups(int argc,char *argv[],void *p);
static int dosafety(int argc,char *argv[],void *p);
static int donntrace(int argc,char *argv[],void *p);
static int doverbose(int argc,char *argv[],void *p);
static int donnstatus(int argc,char *argv[],void *p);
static int donntplist(int argc,char *argv[],void *p);
static int donntpkill(int argc,char *argv[],void *p);
static void error(int level,char *text);
#ifdef NEVER
static void freetextlinelist(struct textline **list);
#endif
static void createkilllists(void);
static void scankilllists(char *line,int *status);
static unsigned int hashmsgid(char *msgid,unsigned int modulus);
static void createhashtable(void);
static int isinhashtable(char *msgid);
static int isinhistory(char *msgid);
static void addtohashtable(char *msgid);
static void addtohistory(char *msgid);
static int stripgroups(char *s);
static unsigned long unitspersecond(unsigned long units,unsigned long milliseconds);
#ifdef NEVER
static int getreply(void);
static int getheader(struct nntprequest *request);
static void moveheadertofile(FILE *fp,struct nntprequest *request);
static int gettxt(FILE *fp,struct nntprequest *request, int newnews);
static int putarticle(FILE *msgf,long int *msgfpos,struct nntprequest *request);
#endif
static void __stdargs nntp_init(int argc,void *argv,void *parentproc);
static void __stdargs nntp_send(int argc,void *argv,void *parentproc);
static void __stdargs nntp_main(int i1,void *tp,void *v1);
static void freetextlinelist(struct textline **);
static void createkilllists(void);
static void createhashtable(void);
static int getreply(void);
static int getheader(struct nntprequest *);
static void moveheadertofile(FILE *, struct nntprequest *);
static int putarticle(FILE *, long int *, struct nntprequest *);
static void nntp_reg (void);
/* Tracing levels:
0 - no tracing
1 - serious errors reported
2 - transient errors reported
3 - session progress reported
4 - actual received articles displayed
*/
static struct cmds Nntpcmds[] = {
{ "addserver", doadds, 0, 3, "nntp addserver <nntpserver> <interval>"},
{ "batch", dobatch, 0, 0, NULLCHAR},
{ "directory", dondir, 0, 0, NULLCHAR},
{ "dropserver", dodrops, 0, 2, "nntp dropserver <nntpserver>"},
{ "groups", dogroups, 0, 0, NULLCHAR},
{ "kick", dokicks, 0, 0, "nntp kick <nntpserver>"},
{ "kill", donntpkill, 0, 2, "kill <jobnumber>"},
{ "listservers", dolists, 0, 0, NULLCHAR},
{ "messages", donntplist, 0, 0, NULLCHAR},
{ "newgroups", donewgroups,0, 0, NULLCHAR},
{ "safety", dosafety, 0, 0, NULLCHAR},
{ "status", donnstatus, 0, 0, NULLCHAR},
{ "trace", donntrace, 0, 0, NULLCHAR},
{ "verbose", doverbose, 0, 0, NULLCHAR},
{ NULLCHAR },
};
int donntp(int argc, char *argv[], void *p)
{
return subcmd(Nntpcmds,argc,argv,p);
}
static int doadds(int argc, char *argv[], void *p)
{
struct nntpservers *np;
for (np = Nntpservers; np != NULLNNTP; np = np->next)
if (stricmp(np->name, argv[1]) == 0)
break;
if (np == NULLNNTP) {
np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
np->name = strdup(argv[1]);
np->next = Nntpservers;
Nntpservers = np;
np->groups = NULLCHAR;
np->lowtime = np->hightime = -1;
np->nntpcli_t.func = nntptick; /* what to call on timeout */
np->nntpcli_t.arg = (void *)np;
}
if (argc > 3) {
int i;
if (np->groups == NULLCHAR) {
np->groups = mallocw(NNTPMAXLEN);
*np->groups = '\0';
}
for (i = 3; i < argc; ++i) {
if (isdigit(*argv[i])) {
int lh, ll, hh, hl;
sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
np->lowtime = lh * 100 + ll;
np->hightime = hh * 100 + hl;
} else if ((strlen(np->groups)+1+strlen(argv[i])) > NNTPMAXGROUPLIST)
tprintf("Group list too long! Group '%s' ignored!\n", argv[i]);
else { /* it's a group, and it fits... add it to list */
if (*np->groups != '\0')
strcat(np->groups, ",");
strcat(np->groups, argv[i]);
}
}
if (*np->groups == '\0') { /* No groups specified? */
free(np->groups);
np->groups = NULLCHAR;
}
}
/* set timer duration */
set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
start_timer(&np->nntpcli_t); /* and fire it up */
return 0;
}
static int dodrops(int argc, char *argv[], void *p)
{
struct nntpservers *np, *npprev = NULLNNTP;
for (np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
if (stricmp(np->name,argv[1]) == 0) {
stop_timer(&np->nntpcli_t);
free(np->name);
if (np->groups)
free(np->groups);
if(npprev != NULLNNTP)
npprev->next = np->next;
else
Nntpservers = np->next;
free((char *)np);
return 0;
}
tprintf("No such server enabled\n");
return 0;
}
static int dolists(int argc, char *argv[], void *p)
{
struct nntpservers *np;
for (np = Nntpservers; np != NULLNNTP; np = np->next) {
char tbuf[80];
if (np->lowtime != -1 && np->hightime != -1)
sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
else
tbuf[0] = '\0';
tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
read_timer(&np->nntpcli_t) /1000L,
dur_timer(&np->nntpcli_t) /1000L,
tbuf, np->groups ? np->groups : "");
}
return 0;
}
static int donntrace(int argc, char *argv[], void *p)
{
return setshort(&nntptrace, "NNTP tracing", argc, argv);
}
static char *News_spool = NULL;
static int np_all = 0; /* non-zero if Newsdir is a malloc'ed space */
static int dondir(int argc, char *argv[], void *p)
{
if (argc < 2) {
tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
tprintf("control: %s\n", Newsdir);
} else {
char *p;
if ((p = strchr(argv[1], '=')) != NULLCHAR) { /* set a groupdir */
tprintf("Directory table not supported\n");
}
else { /* no '=', so just set default */
if (News_spool)
free(News_spool);
News_spool = strdup(argv[1]);
}
if (argc > 2) { /* they specified a newsdir as well */
if (np_all)
free(Newsdir);
Newsdir = strdup(argv[2]);
np_all = 1;
}
}
return 0;
}
int ext_dokicks(void)
{
char *args[] = { "" };
dokicks(1, args, NULL);
return 0;
}
static int dokicks(int argc, char *argv[], void *p)
{
struct nntpservers *np;
for (np = Nntpservers; np != NULLNNTP; np = np->next)
if (argc == 1 || stricmp(np->name,argv[1]) == 0) {
/* If the timer is not running, the timeout function has
* already been called and we don't want to call it again.
*/
if(run_timer(&np->nntpcli_t)) {
stop_timer(&np->nntpcli_t);
nntptick((void *)np);
}
if (argc != 1)
return 0;
}
if (argc != 1)
tprintf("No such server enabled.\n");
return 0;
}
static int dogroups(int argc, char *argv[], void *p)
{
char grouplist[NNTPMAXLEN];
int i;
if (argc < 2) {
if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
tprintf("All groups are currently enabled\n");
else
tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
return 0;
}
if (Nntpgroups != NULLCHAR)
free(Nntpgroups);
*grouplist = '\0';
for (i=1; i < argc; ++i) {
if ((strlen(grouplist)+1+strlen(argv[i])) > NNTPMAXGROUPLIST)
tprintf("Group list too long! Group '%s' ignored!\n", argv[i]);
else {
if(i > 1)
strcat(grouplist,",");
strcat(grouplist,argv[i]);
}
}
Nntpgroups = strdup(grouplist);
return 0;
}
static int dobatch(int argc, char *argv[], void *p)
{
if (argc < 2) {
tprintf("NNTP batch mode: %s, buffers: %d\n",nntpbatch ? "on":"off",nntpbatchsize);
return 0;
}
if (argc > 2) {
nntpbatchsize = atoi(argv[2]);
}
return setbool(&nntpbatch,"NNTP batch mode",argc,argv);
}
static int dosafety(int argc, char *argv[], void *p)
{
return setbool(&nntpsafety, "NNTP safety mode", argc, argv);
}
static int doverbose(int argc, char *argv[], void *p)
{
return setbool(&nntpverbose, "NNTP verbose mode", argc, argv);
}
static int donewgroups(int argc, char *argv[], void *p)
{
return setbool(&nntpnewgroups, "NNTP get new groups", argc, argv);
}
/*
nntptick - This is the routine that gets called every so often to connect to
NNTP servers.
*/
static void nntptick(void *tp)
{
newproc("NNTP client main", 3072, nntp_main, 0, tp, NULL, 0);
}
struct nntprequest {
char msgid[NNTPMAXLEN]; /* id of requested header/body/article */
struct textline *header; /* header text */
unsigned int lines; /* lines in header/body/article */
unsigned long chars; /* characters in header/body/article */
enum {
reqempty = 0, /* vacant request */
reqgetarticle, /* article from get file */
reqnewarticle, /* article from new file */
reqheader, /* header only */
reqbody, /* body to accompany header */
reqkill, /* kill this header */
reqkeep, /* get a body to accompany this header */
reqend, /* no more requests */
reqquit /* close connection */
} status;
struct nntprequest *nextrequest; /* pointer to next request */
};
#define NULLNNTPREQUEST (struct nntprequest *) 0
struct nntpsession {
int s; /* NNTP socket */
struct proc *childproc; /* child process, either init or send */
FILE *getmsgf; /* file holding messages to get */
FILE *newmsgf; /* temporary file for NEWNEWS response */
FILE *historyf; /* history file */
unsigned char *hashtable; /* history hash table */
struct nntprequest
*requesthead, /* request pointer used by sender */
*requesttail; /* request pointer used by receiver */
struct textline
*killlist, /* kill patterns */
*keeplist; /* keep patterns */
int quit; /* if true quit immediately */
unsigned int historyentries; /* number of entries in history file */
unsigned int historyscans; /* number of complete history file scans */
unsigned int getarticles; /* number of lines in get file */
unsigned int getinvalid; /* number of invalid ids from get file */
unsigned int getunavailable; /* number of articles from get file unavailable */
unsigned int getreceived; /* number of complete articles received */
unsigned int newarticles; /* number of new article message ids received */
unsigned int newduplicates; /* number of duplicate articles */
unsigned int newunavailable; /* number of articles unavailalable */
unsigned int newheadersreceived; /* number of headers only received */
unsigned int newreceived; /* number of complete articles received */
unsigned int groupsreceived; /* number of new groups received */
unsigned long receivedarticlechars; /* number of characters in articles received */
unsigned long receivednewschars; /* number of characters in news altogether */
unsigned long receivedtotalchars; /* number of characters received altogether */
unsigned long receivedheaderchars; /* total number of header characters */
unsigned long startsession; /* clock value at start of session */
unsigned long startarticles; /* clock value at start of article fetch */
unsigned long endsession; /* clock value at end of session */
};
#define NULLNNTPSESSION (struct nntpsession *) 0
static struct nntpsession *nntp = NULLNNTPSESSION;
static void error(int level, char *text)
{
char *p; /* -> text */
p = mallocw(strlen (text) + 1);
(void) strcpy(p, text);
rip(p);
if (level <= 2)
log(nntp->s, "%s", p);
if (level <= nntptrace || nntpverbose != 0)
tprintf("%s\n", p);
free (p);
}
static void
freetextlinelist(list)
struct textline **list;
{
struct textline *lastline, *thisline = *list;
while (thisline != NULLTEXTLINE) {
lastline = thisline;
thisline = thisline->nextline;
free(lastline);
}
*list = NULLTEXTLINE;
}
static void
createkilllists()
{
FILE *killf;
char buf[NNTPMAXLEN], *s, *d;
int keep, n, keeplines = 0, killlines = 0;
struct textline *newline, *lastkillline = NULLTEXTLINE, *lastkeepline = NULLTEXTLINE;
/* If it exists, open the kill file */
sprintf(buf, "%s/kill", Newsdir);
if (access(buf, 0) == 0) {
if (mlock(Newsdir, "kill")) {
sprintf(buf, "NNTP Can't lock file %s/kill", Newsdir);
error(2, buf);
nntp->quit = TRUE;
return;
}
if ((killf = fopen(buf, READ_TEXT)) == NULLFILE) {
sprintf(buf, "NNTP Can't open file %s/kill", Newsdir);
error(1, buf);
rmlock(Newsdir, "kill");
nntp->quit = TRUE;
return;
}
while (fgets(buf, NNTPMAXLEN, killf) != NULLCHAR) {
/* strip out extra characters and force lower case */
s = d = buf;
n = 0;
keep = (*s == '!');
if (keep)
s++;
while (*s != '\n' && *s != '\0') {
if (isspace(*s)) {
do s++; while (isspace(*s));
*d++ = ' ';
} else {
*d++ = tolower(*s++);
}
n++;
}
*d = '\0';
if ((newline = (struct textline *) malloc(MINTEXTLINESIZE+n+1)) == NULLTEXTLINE) {
error(2, "NNTP Can't allocate kill buffer");
fclose(killf);
rmlock(Newsdir, "kill");
nntp->quit = TRUE;
return;
}
newline->nextline = NULLTEXTLINE;
memcpy(newline->text, buf, n+1);
if (keep) {
if (lastkeepline == NULLTEXTLINE)
nntp->keeplist = newline;
else
lastkeepline->nextline = newline;
lastkeepline = newline;
keeplines++;
} else {
if (lastkillline == NULLTEXTLINE)
nntp->killlist = newline;
else
lastkillline->nextline = newline;
lastkillline = newline;
killlines++;
}
if (!nntp->quit && !main_exit)
pwait(NULL);
}
if (nntptrace >= 3)
tprintf("NNTP Kill file read: %d kill lines, %d keep lines\n",killlines,keeplines);
fclose(killf);
rmlock(Newsdir, "kill");
}
}
static void
scankilllists(line,status)
char *line;
int *status;
{
char buf[NNTPMAXLEN], *s, *d;
struct textline *thisline;
/* strip space and convert to lower case */
s = line;
d = buf;
while (*s != '\n' && *s != '\0') {
if (isspace(*s)) {
do s++; while (isspace(*s));
*d++ = ' ';
} else {
*d++ = tolower(*s++);
}
}
*d = '\0';
thisline = nntp->keeplist;
while (thisline != NULLTEXTLINE) {
if (wildmat(buf,thisline->text,NULLCHARP)) {
if (nntptrace >= 3)
tprintf("Keep: %s\n %s\n",buf,thisline->text);
*status = reqkeep;
return;
}
thisline = thisline->nextline;
}
if (*status != reqkill) {
thisline = nntp->killlist;
while (thisline != NULLTEXTLINE) {
if (wildmat(buf,thisline->text,NULLCHARP)) {
if (nntptrace >= 3)
tprintf("Kill: %s\n %s\n",buf,thisline->text);
*status = reqkill;
return;
}
thisline = thisline->nextline;
}
}
}
#define HASH1 32749
#define HASH2 32719
unsigned int
hashmsgid(msgid, modulus)
char *msgid;
unsigned int modulus;
{
unsigned long int i = 0;
while (*msgid != '\0') {
do i = (i<<8)|*msgid++; while (*msgid != '\0' && i < (1L<<24));
i %= modulus;
}
return (unsigned int) i;
}
static void
createhashtable()
{
char msgid[NNTPMAXLEN];
unsigned char *hashtable = NULL;
unsigned int i, lines = 0, collisions = 0;
int found;
/* If there is sufficient memory then build a 64K bit hash table from
* the history file.
*/
if (availmem() > Memthresh+(8192*sizeof(unsigned char)) &&
(hashtable = (unsigned char *) calloc(8192,sizeof(unsigned char))) != NULL) {
rewind(nntp->historyf);
while (fgets(msgid,NNTPMAXLEN,nntp->historyf) != NULL) {
lines++;
found = 1;
i = hashmsgid(msgid,HASH1);
if (!(hashtable[i/4]&(0x01<<(i%4)))) {
hashtable[i/4] |= 0x01<<(i%4);
found = 0;
}
i = hashmsgid(msgid,HASH2);
if (!(hashtable[i/4]&(0x10<<(i%4)))) {
hashtable[i/4] |= 0x10<<(i%4);
found = 0;
}
if (found)
collisions++;
if (!nntp->quit && !main_exit)
pwait(NULL);
}
rewind(nntp->historyf);
if (nntptrace >= 3)
tprintf("NNTP Hash table created: %u ids, %u collisions\n", lines, collisions);
}
nntp->hashtable = hashtable;
nntp->historyentries = lines;
nntp->historyscans = 1;
}
static int
isinhashtable(msgid)
char *msgid;
{
unsigned int i, j;
if (nntp->hashtable == NULL)
return 1;
else {
i = hashmsgid(msgid,HASH1);
j = hashmsgid(msgid,HASH2);
if ((nntp->hashtable[i/4]&(0x01<<(i%4))) &&
(nntp->hashtable[j/4]&(0x10<<(j%4))))
return 1;
}
return 0;
}
static int
isinhistory(msgid)
char *msgid;
{
char historyid[NNTPMAXLEN];
long historyfpos;
if (isinhashtable(msgid)) {
historyfpos = ftell(nntp->historyf);
while(fgets(historyid,NNTPMAXLEN,nntp->historyf) != NULLCHAR) {
if(strcmp(historyid,msgid) == 0)
return 1;
if (!nntp->quit && !main_exit)
pwait(NULL);
}
nntp->historyscans++;
rewind(nntp->historyf);
while(fgets(historyid,NNTPMAXLEN,nntp->historyf) != NULLCHAR) {
if(strcmp(historyid,msgid) == 0)
return 1;
if (ftell(nntp->historyf) >= historyfpos)
break;
if (!nntp->quit && !main_exit)
pwait(NULL);
}
}
return 0;
}
static void
addtohashtable(msgid)
char *msgid;
{
unsigned int i, j;
if (nntp->hashtable != NULL) {
i = hashmsgid(msgid,HASH1);
nntp->hashtable[i/4] |= 0x01<<(i%4);
j = hashmsgid(msgid,HASH2);
nntp->hashtable[j/4] |= 0x10<<(j%4);
}
}
static void
addtohistory(msgid)
char *msgid;
{
long historyfpos;
int duphandle;
addtohashtable(msgid);
historyfpos = ftell(nntp->historyf);
fseek(nntp->historyf,0L,SEEK_END);
fputs(msgid,nntp->historyf);
fflush(nntp->historyf);
if (nntpsafety) {
duphandle = dup(fileno(nntp->historyf));
close(duphandle);
}
fseek(nntp->historyf,historyfpos,SEEK_SET);
nntp->historyentries++;
}
/*
* stripgroups reformats a group list in situ for a NEWNEWS request
*
* returns -1 if there is leading space,
* 0 if line is blank otherwise
* n the number of characters in the resultant string
*/
static int
stripgroups(s)
char *s;
{
char *d = s;
int count = 0;
if (!isspace(*s)) return -1;
do s++; while (*s == ',' || isspace(*s));
while (*s != '\0') {
if (count != 0) {*d++ = ','; count++;}
do {
*d++ = *s++; count++;
} while (*s != '\0' && *s != ',' && !isspace(*s));
while (*s == ',' || isspace(*s)) s++;
}
*d = '\0';
return count;
}
static unsigned long
unitspersecond(units,milliseconds)
unsigned long units,milliseconds;
{
if (milliseconds == 0) { /* Avoid divide-by-zero */
return 0;
} else {
if(units < 4294967UL) {
return (units*1000UL)/milliseconds;
} else { /* Avoid overflow */
return units/(milliseconds/1000UL);
}
}
}
/*
getreply - get reply from nntp server
- trace responses at level 3 or higher
*/
static int getreply(void)
{
int n, response;
char buf[NNTPMAXLEN];
while ((n = recvline(nntp->s, buf, NNTPMAXLEN)) != -1) {
nntp->receivedtotalchars += n;
if (nntptrace >= 3) /* write trace message ? */
tprintf("< %s", buf);
/* skip informative messages and blank lines */
if(buf[0] == '\n' || buf[0] == '1')
continue;
sscanf(buf, "%d", &response);
if (response >= 400) {
error (1, buf); /* display error message */
}
return response;
}
return -1;
}
static int
getheader(request)
struct nntprequest *request;
{
char buf[NNTPMAXLEN];
int n, nlines = 0;
unsigned long nchars = 0L;
struct textline *newline, *lastheaderline = NULLTEXTLINE;
unsigned long endtime, starttime = msclock();
while ((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
nntp->receivedtotalchars += n;
nchars += n;
if (nntptrace >= 4)
tprintf("<==%s", buf);
if(strcmp(buf,".\n") == 0) {
request->lines += nlines;
if (nntptrace >= 3) {
endtime = msclock();
tprintf("<--%d lines, %lu chars, %lu sec (%lu bytes/sec)\n",
nlines,
nchars,
(endtime-starttime)/1000UL,
unitspersecond(nchars,endtime-starttime));
}
/* default to getting the body */
if (request->status != reqkill)
request->status = reqkeep;
return nlines;
}
nlines++;
/* check for escaped '.' characters */
#if 0
if(strcmp(buf,"..\n") == 0) {
buf[1] = '\n';
buf[2] = '\0';
n--;
}
#endif
if (*buf == '.') {
memmove(buf, buf + 1, n);
n--;
}
request->chars += n;
if ((newline = (struct textline *) malloc(MINTEXTLINESIZE+n+1)) == NULLTEXTLINE) {
error(2,"NNTP Can't allocate header buffer");
return -1;
}
newline->nextline = NULLTEXTLINE;
memcpy(newline->text,buf,n+1);
if (lastheaderline == NULLTEXTLINE)
request->header = newline;
else
lastheaderline->nextline = newline;
lastheaderline = newline;
if (request->status != reqkeep)
scankilllists(buf,(int *) &(request->status));
}
sprintf(buf,"NNTP Receive error after %d lines", nlines);
error(2,buf);
return -1;
}
static void
moveheadertofile(fp,request)
FILE *fp;
struct nntprequest *request;
{
struct textline *doneline, *thisline = request->header;
while (thisline != NULLTEXTLINE) {
fputs(thisline->text,fp);
doneline = thisline;
thisline = thisline->nextline;
free(doneline);
}
request->header = NULLTEXTLINE;
}
static int gettxt(FILE *fp, struct nntprequest *request, int newnews)
{
char buf[NNTPMAXLEN];
int n, nlines = 0;
unsigned long nchars = 0L;
unsigned long endtime, starttime = msclock();
while ((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
nntp->receivedtotalchars += n;
nchars += n;
if (nntptrace >= 4)
tprintf("<- %s", buf);
if(strcmp(buf,".\n") == 0) {
if (nntptrace < 4 && (newnews != 0 && nntpverbose != 0)) {
tprintf ("\n");
}
if (request != NULLNNTPREQUEST)
request->lines += nlines;
if (nntptrace >= 3) {
endtime = msclock();
tprintf("<--%d lines, %lu chars, %lu sec (%lu bytes/sec)\n",
nlines,
nchars,
(endtime-starttime)/1000UL,
unitspersecond(nchars,endtime-starttime));
}
return nlines;
}
nlines++;
if (nntptrace < 4 && (newnews != 0 && nntpverbose != 0))
tprintf ("\rNews header %d received ", nlines);
/* check for escaped '.' characters */
#if 0
if(strcmp(buf,"..\n") == 0) {
buf[1] = '\n';
buf[2] = '\0';
n--;
}
#endif
if (*buf == '.') {
(void)memmove(buf, buf + 1, n);
n--;
}
if (request != NULLNNTPREQUEST)
request->chars += n;
(void) fputs(buf,fp);
}
if (nntptrace < 4 && (newnews != 0 && nntpverbose != 0))
tprintf ("\n");
sprintf(buf,"NNTP Receive error after %d lines", nlines);
error(2,buf);
return -1;
}
static int
putarticle(msgf,msgfpos,request)
FILE *msgf;
long int *msgfpos;
struct nntprequest *request;
{
char buf[NNTPMAXLEN];
int duphandle;
if (EOF == fflush(msgf)) {
error(1, "NNTP Error writing newsbatch file");
return -1;
}
(void) fseek(msgf,*msgfpos,SEEK_SET);
fprintf(msgf,NEWSBATCHFORMAT,request->chars);
if (EOF == fflush(msgf)) {
error(1, "NNTP Error writing newsbatch file");
return -1;
}
if (nntpsafety) {
duphandle = dup(fileno(msgf));
close(duphandle);
}
if (nntpverbose) {
(void) fseek(msgf,*msgfpos,SEEK_SET);
while(fgets(buf,NNTPMAXLEN,msgf) != NULLCHAR)
if(buf[0] == '\n' || strnicmp(buf,"Newsgroups: ",12) == 0)
break;
if (strnicmp(buf,"Newsgroups: ",12) == 0)
rip(buf+12);
else
buf[12] = '\0';
tprintf("News arrived (%u/%u): %s, %s",
nntp->getreceived + nntp->newheadersreceived + nntp->newreceived + 1,
(nntp->getarticles - nntp->getinvalid - nntp->getunavailable) +
(nntp->newarticles - nntp->newduplicates - nntp->newunavailable),
/* buf+12, */ buf,
request->msgid);
}
/* set new length */
(void) fseek(msgf,0L,SEEK_END);
*msgfpos = ftell(msgf);
fprintf(msgf,NEWSBATCHFORMAT,0UL);
return 0;
}
static void __stdargs
nntp_init(argc,argv,parentproc)
int argc;
void *argv,*parentproc;
{
createhashtable();
createkilllists();
psignal(parentproc,0);
nntp->childproc = NULLPROC;
}
static void __stdargs
nntp_send(argc,argv,parentproc)
int argc;
void *argv,*parentproc;
{
int quit = FALSE;
if (nntp->getmsgf != NULLFILE)
rewind(nntp->getmsgf);
rewind(nntp->newmsgf);
while (!quit) {
switch (nntp->requesthead->status) {
case reqempty:
if (nntp->getmsgf != NULLFILE &&
fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR) {
do { /* Find a line with a valid message id */
char *cp = nntp->requesthead->msgid;
nntp->getarticles++;
if (*cp++ != '<') {
nntp->getinvalid++;
if (!nntp->quit && !main_exit)
pwait(NULL);
continue;
}
while (*cp != '>' && isgraph(*cp))
cp++;
if (*cp++ == '>') {
*cp++ = '\n';
*cp = '\0';
nntp->requesthead->status = reqgetarticle;
break;
}
nntp->getinvalid++;
if (!nntp->quit && !main_exit)
pwait(NULL);
} while (fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR);
}
if (nntp->requesthead->status == reqempty &&
fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->newmsgf) != NULLCHAR) {
do {
if (!isinhistory(nntp->requesthead->msgid)) {
if (nntp->killlist == NULLTEXTLINE)
nntp->requesthead->status = reqnewarticle;
else
nntp->requesthead->status = reqheader;
break;
}
if (nntpverbose)
tprintf("News duplicate: article %s",nntp->requesthead->msgid);
nntp->newduplicates++;
if (!nntp->quit && !main_exit)
pwait(NULL);
} while (fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->newmsgf) != NULLCHAR);
}
if (nntp->requesthead->status == reqempty)
nntp->requesthead->status = reqend;
break;
case reqkeep:
nntp->requesthead->status = reqbody;
break;
case reqend:
nntp->requesthead->status = reqquit;
break;
default: /* should never get here */
error(2,"NNTP Send internal error 1");
nntp->requesthead->status = reqquit;
}
if (!nntpbatch && nntp->requesthead != nntp->requesttail)
pwait(Curproc);
if (nntp->quit || main_exit) {
psignal(parentproc,0);
break;
}
switch (nntp->requesthead->status) {
case reqend:
break;
case reqquit:
if (nntptrace >= 3)
tprintf("==>QUIT\n");
usprintf(nntp->s,"QUIT\n");
quit = TRUE;
break;
case reqgetarticle:
case reqnewarticle:
if (nntptrace >= 3)
tprintf("==>ARTICLE %s",nntp->requesthead->msgid);
usprintf(nntp->s,"ARTICLE %s",nntp->requesthead->msgid);
break;
case reqheader:
if (nntptrace >= 3)
tprintf("==>HEAD %s",nntp->requesthead->msgid);
usprintf(nntp->s,"HEAD %s",nntp->requesthead->msgid);
break;
case reqbody:
if (nntptrace >= 3)
tprintf("==>BODY %s",nntp->requesthead->msgid);
usprintf(nntp->s,"BODY %s",nntp->requesthead->msgid);
break;
default: /* should never get here */
error(2,"NNTP Send internal error 2");
if (nntptrace >= 3)
tprintf("==>QUIT\n");
usprintf(nntp->s,"QUIT\n");
quit = TRUE;
}
if (nntpbatch && nntp->requesthead->nextrequest == nntp->requesttail)
pwait(Curproc);
nntp->requesthead = nntp->requesthead->nextrequest;
psignal(parentproc,0);
}
/* let the folks know we've gone */
nntp->childproc = NULLPROC;
}
/*
nntpmain - nntp main routine
*/
static void __stdargs nntp_main(int i1, void *tp, void *v1)
{
int reply, lines, i;
char buf1[NNTPMAXLEN], buf2[NNTPMAXLEN], *cp;
struct nntpservers *np = (struct nntpservers *)tp;
struct sockaddr_in fsocket;
int post_ok; /* code to post via NNTP - ATARI */
char *cp1;
char nnfix[9];
char wgroup[80];
char wmsgid[80];
char atmp[LINELEN];
char wtmp[LINELEN];
char ttmp[LINELEN];
char wfilename[13];
FILE *wfile;
FILE *tfile;
FILE *nntp_dat; /* handle for nntp.dat */
FILE *tmpf1, *tmpf2, *gettmpf, *msgf = NULLFILE;
long int tmpfpos, msgfpos;
char datenow[14], lastdate[14];
if (nntptrace >= 3)
tprintf("NNTP daemon entered, target = %s\n", np->name);
if (nntp != NULLNNTPSESSION) { /* nntp already active */
error(2, "NNTP Already active");
start_timer(&np->nntpcli_t);
return;
}
{ /* check the time - keep a record for updating the data file */
time_t t;
struct tm *ltm;
int now;
time(&t); /* more portable than gettime() */
t -= 120L; /* 2 minute fudge factor */
ltm = localtime(&t);
now = ltm->tm_hour * 100 + ltm->tm_min;
if ((np->lowtime < np->hightime) ? /* doesn't cross midnight */
(now < np->lowtime || now >= np->hightime) :
(now < np->lowtime && now >= np->hightime)) {
sprintf(buf2, "NNTP Window to '%s' not open", np->name);
error(2, buf2);
start_timer(&np->nntpcli_t);
return;
}
ltm = gmtime(&t);
sprintf(datenow, "%02d%02d%02d %02d%02d%02d", ltm->tm_year%100,
ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
}
if (availmem() < Memthresh+sizeof(struct nntpsession) ||
(nntp = (struct nntpsession *)calloc(1, sizeof(struct nntpsession))) == NULLNNTPSESSION) {
error(2, "NNTP Memory too low");
start_timer(&np->nntpcli_t); /* restart nntp ick timer */
return;
}
nntp->s = -1;
if (nntpverbose)
tprintf("Trying to connect to news server %s ....\n",np->name);
/* set up a circular list of request buffers */
{
struct nntprequest *head, *tail;
if (availmem() < Memthresh+sizeof(struct nntprequest) ||
(head = tail =
(struct nntprequest *)calloc(1, sizeof(struct nntprequest))) == NULLNNTPREQUEST) {
error(2, "NNTP Memory too low");
goto quit;
}
for (i = 1; i < nntpbatchsize || i < 2; i++) {
if (availmem() < Memthresh+sizeof(struct nntprequest) ||
(tail->nextrequest =
(struct nntprequest *)calloc(1, sizeof(struct nntprequest))) == NULLNNTPREQUEST) {
error(2, "NNTP Memory too low");
goto quit;
}
tail = tail->nextrequest;
}
nntp->requesttail = nntp->requesthead = tail->nextrequest = head;
}
/* Find the server and try to connect */
fsocket.sin_addr.s_addr = resolve(np->name);
if(fsocket.sin_addr.s_addr == 0) { /* No IP address found */
sprintf(buf2, "NNTP Can't resolve host: %s", np->name);
error(2, buf2);
goto quit;
}
fsocket.sin_family = AF_INET; /* setup socket */
fsocket.sin_port = IPPORT_NNTP;
nntp->s = socket(AF_INET,SOCK_STREAM,0);
sockmode(nntp->s,SOCK_ASCII);
alarm (connect_wait_val); /* connection timeout alarm */
if (connect(nntp->s, (char *)&fsocket,SOCKSIZE) == -1) {
if (!run_timer(&Curproc->alarm))
sprintf(buf2, "NNTP Connect failed: timeout");
else {
alarm (0L); /* reset alarm */
cp = sockerr(nntp->s);
sprintf(buf2, "NNTP Connect failed: %s", cp != NULLCHAR ? cp : "reason unknown");
}
error(2, buf2);
goto quit; /* quit - connect failed connect */
}
alarm (0L); /* reset alarm */
nntp->startsession = msclock(); /* store session start time */
nntp->startarticles = 0; /* zero article start time */
if (nntpverbose)
tprintf("Connected to news server\n");
/* Open the history file */
sprintf(buf1, "%s/history", Newsdir);
if (mlock(Newsdir, "history")) {
sprintf(buf2, "NNTP Can't lock file %s", buf1);
error(2, buf2);
goto quit; /* quit -can't lock history file */
}
if ((nntp->historyf = fopen(buf1, APPEND_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
rmlock(Newsdir, "history");
goto quit; /* quit - can't open history */
}
/* spawn a process to build the internal tables */
nntp->childproc = newproc("NNTP client init", 1024, nntp_init, 0, NULL, Curproc, 0);
reply = getreply(); /* get banner from nntp server */
if(!(reply == 200 || reply == 201)) {
sprintf(buf2, "NNTP Bad reply on banner (response was %d)", reply);
error(1, buf2);
goto quit; /* quit - bad banner */
}
/* --- ATARI Code to support posting of Articles - from ANOS NNTP ---------*/
if (reply == 201) {
if (nntptrace >= 1)
tprintf("NNTP - No posting allowed on '%s'\n", np->name);
}
else {
for (filedir(Newsqueue, 0, wfilename);
wfilename[0] != '\0';
filedir(Newsqueue, 1, wfilename)) {
cp = wfilename;
cp1 = nnfix;
while (*cp && *cp != '.')
*cp1++ = *cp++;
*cp1 = '\0';
sprintf(wtmp, "%s/%s", Newsqdir, wfilename);
if ((wfile = fopen(wtmp,READ_TEXT)) == NULLFILE)
continue;
(void) fgets(wmsgid,LINELEN,wfile); /* read messageid */
rip(wmsgid);
/* Validate the target groups - post if 1 or more is valid */
post_ok = FALSE;
while (fgets(wgroup, LINELEN, wfile)) { /* read target group */
rip(wgroup);
if (nntptrace >= 3) /* write trace message ? */
tprintf("> GROUP %s\n", wgroup);
usprintf(nntp->s, "GROUP %s\n", wgroup); /* send query */
reply = getreply(); /* get reply from server */
if (reply == 411) {
if (nntptrace >= 1)
tprintf("NNTP No Such Newsgroup '%s'\n", wgroup);
continue; /* try next group in the list */
}
if (reply == 211)
post_ok = TRUE; /* newsgroup OK */
}
fclose(wfile);
if (post_ok) { /* post if 1 or more valid groups*/
if (nntptrace >= 3) /* write trace message ? */
tprintf("> IHAVE %s \n", wmsgid);
usprintf(nntp->s, "IHAVE %s\n", wmsgid); /* offer item */
reply = getreply(); /* get reply from server */
if (reply == 335) { /* send new article */
sprintf(atmp, "%s/%s.txt", Newsqdir, nnfix);
if ((tfile = fopen(atmp, READ_TEXT)) != NULLFILE) {
while(fgets(ttmp, LINELEN, tfile) != NULLCHAR) {
if (nntptrace >= 4) /* write trace message ? */
tprintf("> ttmp");
usprintf(nntp->s,ttmp); /* send line of article */
}
usprintf(nntp->s, ".\n"); /* send end-of-text marker */
fclose(tfile);
}
reply = getreply(); /* article posted, get rep */
if(reply == 235) { /* posting OK ? */
remove(atmp); /* remove article and */
remove(wtmp); /* work files */
}
}
if(reply == 436)
if (nntptrace >= 1)
tprintf("NNTP '%s' Transfer Failed - Try later\n", wmsgid);
if(reply == 435) {
if (nntptrace >= 1)
tprintf("NNTP Article '%s' Not wanted\n", wmsgid);
remove(wtmp);
}
if(reply == 437) {
if (nntptrace >= 1)
tprintf("NNTP Article '%s' Rejected\n", wmsgid);
remove(wtmp);
}
}
}
}
/*----------------------------------------------------------------------*/
/* Now fetch new messages */
/*----------------------------------------------------------------------*/
/* Open nntp.dat */
sprintf(buf1, "%s/nntp.dat", Newsdir);
if (mlock(Newsdir, "nntp")) {
sprintf(buf2, "NNTP Can't lock file %s", buf1);
error(2, buf2);
goto quit; /* quit, can't lock nntp.dat */
}
if ((nntp_dat = fopen(buf1, APPEND_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
rmlock(Newsdir, "nntp");
goto quit; /* quit, cant' open nntp.dat */
}
/* Open temporary file for message ids */
if ((nntp->newmsgf = tmpfile()) == NULLFILE) {
error(1, "NNTP Can't open temporary file for message ids");
fclose(nntp_dat);
rmlock(Newsdir,"nntp");
goto quit; /* quit, can't open msgid file */
}
/* Look for a line in the data file with a matching server name */
rewind(nntp_dat);
strcpy(lastdate, "700101 000000");
i = strlen(np->name);
cp = buf1 + i;
while (fgets(buf1, NNTPMAXLEN, nntp_dat) != NULLCHAR) {
if (strnicmp(buf1, np->name, i) == 0 && isspace(*cp)) {
unsigned long date, time;
if (sscanf(cp, " %6lu %6lu", &date, &time) == 2)
sprintf(lastdate, "%06lu %06lu", date, time);
break;
}
}
/* Look for a newsgroup list in the data file */
i = -1;
while (fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR)
if ((i = stripgroups(buf2)) != 0)
break;
/* if there is a newsgroup list in the data file */
if (i > 0) {
do {
if (i > NNTPMAXGROUPLIST) {
error(1, "NNTP Newsgroup line too long");
fclose(nntp_dat);
rmlock(Newsdir, "nntp"); /* free lock on nntp.dat */
goto quit; /* quit, NG line too long */
}
strcpy(buf1, buf2);
i = -1;
/* add entries until no more entries, blank line or the list is full */
while ((fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR) &&
((i = stripgroups(buf2)) > 0))
if (strlen(buf1)+i >= NNTPMAXGROUPLIST)
break;
else {
strcat(buf1,",");
strcat(buf1,buf2); i = -1;
}
/* send the request and get the response */
if (nntptrace >= 3)
tprintf("> NEWNEWS %s %s GMT\n", buf1, lastdate);
#if 0
nntp_reg();
#endif
usprintf(nntp->s, "NEWNEWS %s %s GMT\n", buf1, lastdate);
if ((reply = getreply()) != 230 ||
(lines = gettxt(nntp->newmsgf, NULL, 1)) == -1) {
sprintf(buf2, "NNTP Error while getting NEWNEWS (response was %d)",
reply);
error(1, buf2);
fclose(nntp_dat); /* close nntp.dat */
rmlock(Newsdir, "nntp"); /* remove lock on nntp.dat */
goto quit; /* quit, NEWNEWS error */
}
nntp->newarticles += lines;
/* if we're on a blank line then look for more newsgroup entries */
if (i == 0) {
i = -1;
while (fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR)
if ((i = stripgroups(buf2)) != 0)
break;
}
} while (i > 0);
} else { /* use the addservers entry, or the groups entry, or get everything (!) */
if (nntptrace >= 3)
tprintf("> NEWNEWS %s %s GMT\n",
np->groups ? np->groups : (Nntpgroups ? Nntpgroups : "*"),
lastdate);
usprintf(nntp->s, "NEWNEWS %s %s GMT\n",
np->groups ? np->groups : (Nntpgroups ? Nntpgroups : "*"),
lastdate);
if ((reply = getreply()) != 230 || (lines = gettxt(nntp->newmsgf,NULL, 1)) == -1) {
sprintf(buf2,"NNTP Error while getting NEWNEWS (response was %d)",reply);
error(1,buf2);
fclose(nntp_dat); /* close nntp.dat */
rmlock(Newsdir,"nntp"); /* remove lock on nntp.dat */
goto quit; /* quit, NEWNEWS error */
}
nntp->newarticles = lines;
}
fclose(nntp_dat); /* close nntp.dat */
rmlock(Newsdir,"nntp"); /* remove lock on nntp.dat */
if (nntp->quit || main_exit)
goto quit;
if (nntpverbose && nntp->newarticles != 0)
tprintf("News available: %d articles\n", nntp->newarticles);
if (nntpnewgroups) {
/* Open the newgroups file */
sprintf(buf1, "%s/newgroup", Newsdir);
if (mlock(Newsdir, "newgroup")) {
sprintf(buf2, "NNTP Can't lock file %s", buf1);
error(2, buf2);
goto quit; /* quit, can't lock newgroups */
}
if ((tmpf1 = fopen(buf1, APPEND_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
rmlock(Newsdir, "newgroup"); /* remove lock on newgroup file */
goto quit; /* quit, can't open newgroup */
}
if (nntptrace >= 3) /* write tracefile ? */
tprintf("> NEWGROUPS %s GMT\n", lastdate);
usprintf(nntp->s, "NEWGROUPS %s GMT\n", lastdate);
if ((reply = getreply()) != 231 || (lines = gettxt(tmpf1,NULL, 0)) == -1) {
sprintf(buf2,"NNTP Error while getting NEWGROUPS (response was %d)",reply);
error(1,buf2); /* report error */
fclose(tmpf1); /* close newgroups file */
rmlock(Newsdir,"newgroup"); /* remove lock on newgroups file */
goto quit; /* quit, NEWGROUPS error */
}
fflush(tmpf1);
fseek(tmpf1, 0L, SEEK_END);
tmpfpos = ftell(tmpf1);
fclose(tmpf1);
if (tmpfpos == 0L) {
sprintf(buf1,"%s/newgroup",Newsdir);
remove(buf1);
}
rmlock(Newsdir,"newgroup"); /* remove lock on newgroups file */
nntp->groupsreceived = lines;
if (nntpverbose && nntp->groupsreceived != 0)
tprintf("News available: %d new groups\n", nntp->groupsreceived);
}
/* If it exists, open the get file */
sprintf(buf1,"%s/get",Newsdir);
if (access(buf1, 0) == 0) {
if (mlock(Newsdir,"get")) {
sprintf(buf2,"NNTP Can't lock file %s",buf1);
error(2, buf2);
} else {
if ((nntp->getmsgf = fopen(buf1, READ_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
rmlock(Newsdir, "get");
} else {
/* Open temporary file for unavailable message ids */
sprintf(buf1, "%s/get.tmp", Newsdir);
if((gettmpf = fopen(buf1,WRITE_TEXT)) == NULLFILE) {
sprintf(buf2,"NNTP Can't open file %s",buf1);
error(1, buf2);
fclose(nntp->getmsgf);
nntp->getmsgf = NULLFILE;
rmlock(Newsdir, "get");
}
}
}
}
/* Open the batch file */
sprintf(buf1, "%s/batch.txt", News_spool ? News_spool : Mailspool);
if (mlock(News_spool ? News_spool : Mailspool, "batch")) {
sprintf(buf2, "NNTP Can't lock file %s", buf1);
error(2, buf2);
goto quit; /* quit, can't lock batch file */
}
if((msgf = fopen(buf1, RW_LOOKUP_TEXT)) == NULLFILE &&
(msgf = fopen(buf1, RW_CREATE_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
rmlock(News_spool ? News_spool : Mailspool, "batch");
goto quit; /* quit, can't open batch file */
}
fseek(msgf, 0L, SEEK_END);
msgfpos = ftell(msgf);
fprintf(msgf, NEWSBATCHFORMAT, 0UL);
/* wait for the tables to be built then start up the tx proc */
if (nntp->childproc != NULLPROC) /* wait for init task to finish */
if (pwait(Curproc) != 0)
goto quit;
if (nntp->quit || main_exit)
goto quit;
nntp->childproc = newproc("NNTP client send", 1024, nntp_send, 0, NULL, Curproc, 0);
nntp->receivednewschars = nntp->receivedtotalchars;
nntp->receivedheaderchars = nntp->receivedtotalchars;
nntp->startarticles = msclock();
while (!nntp->quit && !main_exit) {
if (nntp->requesthead == nntp->requesttail) {
if (nntp->childproc == NULLPROC) {
error(2, "NNTP Receive internal error");
goto quit; /* quit, internel receive error */
}
if (pwait(Curproc) != 0)
goto quit;
}
if (nntp->requesttail->status != reqend) {
reply = getreply();
switch (reply) {
case 205: /* closing connection */
if (nntp->requesttail->status != reqquit) {
error(2, "NNTP Receive internal error (reply was 205)");
goto quit; /* quit, internel receive error */
}
sprintf(buf1, "%s/nntp.dat", Newsdir);
if (mlock(Newsdir, "nntp")) {
sprintf(buf2, "NNTP Can't lock file %s", buf1);
error(2, buf2);
goto quit; /* quit, can't lock nntp.dat */
}
if((nntp_dat = fopen(buf1, READ_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
rmlock(Newsdir, "nntp"); /* remove nntp.dat lockfile */
goto quit; /* quit, can't open nntp.dat */
}
sprintf(buf1,"%s/nntp.tmp", Newsdir);
if((tmpf2 = fopen(buf1,WRITE_TEXT)) == NULLFILE) {
sprintf(buf2, "NNTP Can't open file %s", buf1);
error(1, buf2);
fclose(nntp_dat); /* close nntp.dat */
rmlock(Newsdir, "nntp"); /* remove lock */
goto quit; /* quit, can't open nntp.tmp */
}
sprintf(buf1, "%s %s\n", np->name, datenow);
i = strlen(np->name);
cp = buf2 + i;
while (fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR)
if (strnicmp(buf2,np->name,i) != 0 || !isspace(*cp))
fputs(buf2, tmpf2);
else {
fputs(buf1, tmpf2);
buf1[0] = '\0';
}
if (buf1[0] != '\0')
fputs(buf1,tmpf2);
fclose(nntp_dat);
sprintf(buf1, "%s/nntp.dat", Newsdir);
sprintf(buf2, "%s/nntp.tmp", Newsdir);
if (ferror(tmpf2)) {
fclose(tmpf2);
remove(buf2);
error(1, "Error writing new nntp.dat file");
} else {
fclose(tmpf2);
remove(buf1);
rename(buf2, buf1);
}
rmlock(Newsdir,"nntp"); /* remove lock on nntp.dat */
goto quit;
case 220: /* header and body follows */
if (nntp->requesttail->status != reqgetarticle &&
nntp->requesttail->status != reqnewarticle) {
error(2, "NNTP Receive internal error (reply was 220)");
goto quit; /* quit, internel receive error */
}
nntp->requesttail->lines = 0;
nntp->requesttail->chars = 0UL;
if (gettxt(msgf, nntp->requesttail, 0) == -1)
goto quit;
if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
goto quit;
/*
* The following is a compromise: if there is a hash
* collision then the id will erroneously not get added.
* However, scanning the history file here will cause
* problems as the child might be doing the same.
*/
if (nntp->requesttail->status == reqgetarticle) {
if (!isinhashtable(nntp->requesttail->msgid))
addtohistory(nntp->requesttail->msgid);
nntp->getreceived++;
} else {
addtohistory(nntp->requesttail->msgid);
nntp->newreceived++;
}
nntp->receivedarticlechars += nntp->requesttail->chars;
nntp->requesttail->status = reqempty;
break;
case 221: /* header follows */
if (nntp->requesttail->status != reqheader) {
error(2, "NNTP Receive internal error (reply was 221)");
goto quit; /* quit, internel receive error */
}
nntp->requesttail->lines = 0;
nntp->requesttail->chars = 0UL;
if (getheader(nntp->requesttail) == -1)
goto quit;
if (nntp->requesttail->status == reqkill) {
moveheadertofile(msgf,nntp->requesttail);
if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
goto quit;
addtohistory(nntp->requesttail->msgid);
nntp->newheadersreceived++;
nntp->receivedarticlechars += nntp->requesttail->chars;
nntp->requesttail->status = reqempty;
}
break;
case 222: /* body follows */
if (nntp->requesttail->status != reqbody) {
error(2, "NNTP Receive internal error (reply was 222)");
goto quit; /* quit, internal receive error */
}
moveheadertofile(msgf,nntp->requesttail);
fputc('\n', msgf);
nntp->requesttail->lines++;
nntp->requesttail->chars++;
if (gettxt(msgf,nntp->requesttail, 0) == -1)
goto quit;
if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
goto quit;
addtohistory(nntp->requesttail->msgid);
nntp->newreceived++;
nntp->receivedarticlechars += nntp->requesttail->chars;
nntp->requesttail->status = reqempty;
break;
case 430: /* no such article */
if (nntp->requesttail->status != reqgetarticle &&
nntp->requesttail->status != reqnewarticle &&
nntp->requesttail->status != reqheader) {
error(2,"NNTP Receive internal error (reply was 430)");
goto quit; /* quit, internal receive error */
}
if (nntp->requesttail->status == reqgetarticle) {
fputs(nntp->requesttail->msgid,gettmpf);
nntp->getunavailable++;
} else
nntp->newunavailable++;
if (nntpverbose)
tprintf("News unavailable: article %s",nntp->requesttail->msgid);
nntp->requesttail->status = reqempty;
break;
case -1: /* error */
error(2, "NNTP Receive error");
goto quit; /* quit, NNTP receive error */
default:
sprintf(buf2, "NNTP Unexpected reply from server (reply was %d)",reply);
error(2, buf2);
goto quit; /* quit, unexpected reply */
}
}
/* move on to next request */
nntp->requesttail = nntp->requesttail->nextrequest;
psignal(nntp->childproc, 0);
}
/* quit nntp session */
quit:
nntp->endsession = msclock();
nntp->receivednewschars = nntp->receivedtotalchars - nntp->receivednewschars;
/* tidy up as necessary */
if (msgf != NULLFILE) {
fflush(msgf);
fclose(msgf);
if (msgfpos == 0L) {
sprintf(buf1, "%s/batch.txt",News_spool ? News_spool : Mailspool);
(void) remove(buf1);
}
rmlock(News_spool ? News_spool : Mailspool, "batch");
}
else {
if (msgf != NULLFILE) {
fclose(msgf);
}
}
nntp->quit = TRUE;
if (nntp->childproc != NULLPROC)
pwait(NULL);
if (nntp->childproc != NULLPROC)
killproc(nntp->childproc);
/* Update get file with message ids we didn't get */
if (nntp->getmsgf != NULLFILE) {
rewind(nntp->getmsgf);
for (i = 0;
i < (nntp->getinvalid + nntp->getunavailable + nntp->getreceived);
i++)
fgets(buf1, NNTPMAXLEN, nntp->getmsgf);
while (fgets(buf1,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR)
fputs(buf1, gettmpf);
fclose(nntp->getmsgf);
sprintf(buf1, "%s/get", Newsdir);
sprintf(buf2, "%s/get.tmp", Newsdir);
if (ferror(gettmpf)) {
fclose(gettmpf);
remove(buf2);
error(1,"Error writing new get file");
} else {
remove(buf1);
fflush(gettmpf);
fseek(gettmpf,0L,SEEK_END);
tmpfpos = ftell(gettmpf);
fclose(gettmpf);
if (tmpfpos == 0L)
remove(buf2);
else
rename(buf2, buf1);
}
rmlock(Newsdir,"get");
}
if (nntp->newmsgf != NULLFILE)
fclose(nntp->newmsgf);
if (nntp->historyf != NULLFILE) {
fclose(nntp->historyf);
rmlock(Newsdir, "history");
}
if (nntp->hashtable != NULL)
free(nntp->hashtable);
/* Free all dynamically allocated buffer space */
{
struct nntprequest
*tail = nntp->requesthead,
*head = tail->nextrequest;
tail->nextrequest = NULLNNTPREQUEST;
while (head != NULLNNTPREQUEST) {
freetextlinelist(&head->header);
tail = head;
head = head->nextrequest;
free(tail);
}
freetextlinelist(&nntp->killlist);
freetextlinelist(&nntp->keeplist);
}
/* Output statistics */
if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0) {
sprintf(buf2,"News summary: %u articles (%lu bytes) in %lu sec (%lu bytes/sec)",
nntp->getreceived + nntp->newheadersreceived + nntp->newreceived,
nntp->receivedarticlechars,
(nntp->endsession - nntp->startarticles)/1000UL,
unitspersecond(nntp->receivedarticlechars,nntp->endsession - nntp->startarticles));
log(nntp->s,buf2);
tprintf(nntpverbose ? "\n%s\n" : "%s\n",buf2);
}
if (nntp->getarticles != 0) {
sprintf(buf2,"Get articles: %u invalid, %u unavailable, %u received",
nntp->getinvalid,
nntp->getunavailable,
nntp->getreceived);
log(nntp->s,buf2);
if (nntpverbose)
tprintf("%s\n",buf2);
}
if (nntp->newarticles != 0) {
sprintf(buf2,"New articles: %u duplicate, %u unavailable, %u headers, %u complete",
nntp->newduplicates,
nntp->newunavailable,
nntp->newheadersreceived,
nntp->newreceived);
log(nntp->s,buf2);
if (nntpverbose)
tprintf("%s\n",buf2);
}
if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0 ||
nntp->getarticles != 0 || nntp->newarticles != 0) {
sprintf(buf2,"History file: %u entries, %u complete scans",
nntp->historyentries,
nntp->historyscans);
log(nntp->s,buf2);
if (nntpverbose)
tprintf("%s\n",buf2);
sprintf(buf2,"Throughput : %lu/%lu bytes in %lu/%lu sec (%lu/%lu bytes/sec)",
nntp->receivednewschars,
nntp->receivedtotalchars,
(nntp->endsession - nntp->startarticles)/1000UL,
(nntp->endsession - nntp->startsession)/1000UL,
unitspersecond(nntp->receivednewschars,nntp->endsession - nntp->startarticles),
unitspersecond(nntp->receivedtotalchars,nntp->endsession - nntp->startsession));
log(nntp->s,buf2);
if (nntpverbose)
tprintf("%s\n\n",buf2);
}
if (nntpverbose)
tprintf("Closing news session\n");
close_s(nntp->s);
free(nntp);
nntp = NULLNNTPSESSION;
if (nntptrace >= 3)
tprintf("NNTP daemon exiting\n");
/* Restart timer */
start_timer(&np->nntpcli_t);
return;
}
/*
nntp_req
*/
static void nntp_reg(void)
{
struct sockaddr_in sock;
struct socket lsocket;
struct socket rsocket;
struct usock *usp;
struct sockaddr_in *sinp;
struct mbuf *bp;
char buf[64];
int s;
sock.sin_family = AF_INET;
sock.sin_port = 2911;
if ((sock.sin_addr.s_addr = resolve ("passwd.demon.co.uk")) == 0 ||
(s = socket (AF_INET, SOCK_DGRAM, 0)) == -1 ||
connect (s, (char *) &sock, sizeof (sock)) == -1)
return;
(void) sprintf (buf, "%s|%s|%s\n", Version, __DATE__, __TIME__);
bp = ambufw (strlen (buf) + 1);
(void) strcpy (bp->data, buf);
usp = itop(s);
sinp = (struct sockaddr_in *) usp->name;
lsocket.address = sinp->sin_addr.s_addr;
lsocket.port = sinp->sin_port;
sinp = (struct sockaddr_in *)usp->peername;
rsocket.address = sinp->sin_addr.s_addr;
(void) send_udp (&lsocket, &rsocket, 0, 64, bp, bp->size, 0, 0);
close_s (s);
}
/*
donnstats - Print NNTP statistics for current session
*/
static int donnstatus(int argc, char *argv[], void *p)
{
unsigned long timenow;
if (nntp == NULLNNTPSESSION) {
tprintf("News: No session active \n");
return 0;
}
timenow = msclock();
if (nntp->startarticles == 0) { /* Collecting headers */
tprintf("NNTP: Fetching new headers: Time %lu sec, Rate %lu chars/sec \n",
(timenow - nntp->startsession) / 1000UL,
unitspersecond(nntp->receivedtotalchars, timenow - nntp->startsession));
#if 0
tprintf(" %d headers received, %d duplicate, in %lu sec \n\n",
nntp->newarticles, nntp->newduplicates,
(timenow - nntp->startsession) / 1000UL );
#endif
return 0;
}
tprintf("NNTP: Fetching articles: Time %lu sec, Rate %lu chars/sec\n",
(timenow - nntp->startarticles) / 1000UL,
unitspersecond(nntp->receivedtotalchars - nntp->receivedheaderchars,
timenow - nntp->startarticles ));
tprintf(" %d headers received, %d duplicate, in %lu sec\n",
nntp->newarticles, nntp->newduplicates,
(nntp->startarticles - nntp->startsession) / 1000UL );
if (nntp->getreceived > 0)
tprintf(" %d/%d get articles received, %d invalid, %d unavailable \n",
nntp->getreceived,
nntp->getarticles - nntp->getinvalid - nntp->getunavailable,
nntp->getinvalid,
nntp->getunavailable);
if (nntp->newreceived > 0)
tprintf(" %d/%d new articles received, %d duplicates, %d unavailable \n",
nntp->newreceived,
nntp->newarticles - nntp->newduplicates - nntp->newunavailable,
nntp->newduplicates,
nntp->newunavailable);
#if 0
if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0) {
tprintf("\nNews summary: %u/%u articles received \n",
nntp->getreceived + nntp->newheadersreceived + nntp->newreceived,
(nntp->getarticles - nntp->getinvalid - nntp->getunavailable) +
(nntp->newarticles - nntp->newduplicates - nntp->newunavailable));
}
if (nntp->getarticles != 0) {
tprintf("Get articles: %u invalid, %u unavailable, %u received\n",
nntp->getinvalid,
nntp->getunavailable,
nntp->getreceived);
}
if (nntp->newarticles != 0) {
tprintf("New articles: %u duplicate, %u unavailable, %u headers, %u complete \n",
nntp->newduplicates,
nntp->newunavailable,
nntp->newheadersreceived,
nntp->newreceived);
}
#endif
if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0 ||
nntp->getarticles != 0 || nntp->newarticles != 0) {
tprintf(" History file: %u entries, %u complete scans \n",
nntp->historyentries,
nntp->historyscans);
}
return 0;
}
/*
donntplist - list messages wating to be sent
*/
static int donntplist(int argc, char *argv[], void *p)
{
char tstring[80], line[20],
group[LINELEN], *cp;
struct stat stbuf;
struct tm *tminfo;
FILE *fp;
int statx(const char *, struct stat *);
Current->flowmode = 1; /* Enable the more mechanism */
tprintf(" Job Size Date Time Message ID\n");
filedir(Newsqueue,0,line);
while(line[0] != '\0') {
sprintf(tstring,"%s/%s",Newsqdir,line);
if ((fp = fopen(tstring,READ_TEXT)) == NULLFILE) {
tprintf("Can't open %s: %s\n",tstring,strerror(errno));
continue;
}
if ((cp = strrchr(line,'.')) != NULLCHAR)
*cp = '\0';
sprintf(tstring,"%s/%s.txt",Newsqdir,line);
statx(tstring,&stbuf); /* call modified stat routine */
tminfo = localtime(&stbuf.st_ctime);
fgets(group,sizeof(group),fp);
rip(group);
tprintf("%7s %7ld %02d/%02d %02d:%02d %-25s\n",
line, stbuf.st_size,
tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour,
tminfo->tm_min, group);
(void) fclose(fp);
pwait(NULL);
filedir(Newsqueue,1,line);
}
Current->flowmode = 0;
return 0;
}
/* kill a job in the mqueue */
static int donntpkill(int argc, char *argv[], void *p)
{
char s[LINELEN];
char *cp;
sprintf(s,"%s/%s.nwk",Newsqdir,argv[1]);
cp = strrchr(s,'.');
if (remove(s))
tprintf("Job id %s not found\n",argv[1]);
strcpy(cp,".txt");
(void) remove(s);
return 0;
}